package com.base.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import javax.servlet.http.HttpServletResponse;

public class ZipTool {

	public static void main(String[] args) throws IOException {
		//zipFilesInPath("D:\\yanghao\\test-mm1.zip","D:\\yanghao\\html5\\webApp3dP\\server.js");
		File file = new File("D:\\yanghao\\html5\\webApp3dP\\server.js");
		System.out.println(file.getAbsolutePath());
	}

	private static final String PATH_SEP = File.separator;
	private static final int BUFFER = 2048;


	public static void downZipFiles(HttpServletResponse response,
			final String filePath) throws Exception {
		response.setCharacterEncoding("utf-8");  
		response.setContentType("multipart/form-data");  
		response.setHeader("Content-Disposition", "attachment;fileName="+JUtil.getDateStr(new Date(), "yyyyMMddHHmmSS").concat(JUtil.getRandom(100)+"").concat(".zip")); 
		final ZipOutputStream out = new ZipOutputStream(
				new BufferedOutputStream(response.getOutputStream()));
		try {
			final File folder = new File(filePath);
			dozipFilesInPath(out, folder, "");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			out.close();
		}
	}
	
	
	public static void downZipFiles(HttpServletResponse response,
			List<File> list) throws Exception {
		response.setCharacterEncoding("utf-8");  
		response.setContentType("multipart/form-data");  
		response.setHeader("Content-Disposition", "attachment;fileName="+JUtil.getDateStr(new Date(), "yyyyMMddHHmmSS").concat(JUtil.getRandom(100)+"").concat(".zip")); 
		final ZipOutputStream out = new ZipOutputStream(
				new BufferedOutputStream(response.getOutputStream()));
		try {
			dozipFilesInPath(out, list);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			out.close();
		}
	}

	public static void zipFilesInPath(final String zipFileName,
			final String filePath) throws IOException {
		final FileOutputStream dest = new FileOutputStream(zipFileName);
		final ZipOutputStream out = new ZipOutputStream(
				new BufferedOutputStream(dest));
		try {
			final File folder = new File(filePath);
			dozipFilesInPath(out, folder, "");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			out.close();
			dest.close();
		}
	}
	
	/**
	 * @param out
	 * @param file
	 * @param path
	 * @throws Exception
	 */
	public static void dozipFilesInPath(ZipOutputStream out, List<File> files)
			throws Exception {
		for(File file:files){
			if (file.isFile()) {
				byte[] data = new byte[BUFFER];
				final FileInputStream fi = new FileInputStream(file);
				final BufferedInputStream origin = new BufferedInputStream(fi,
						BUFFER);
				out.putNextEntry(new ZipEntry(file.getName()));
				int count;
				while ((count = origin.read(data, 0, BUFFER)) != -1) {
					out.write(data, 0, count);
				}
				origin.close();
				fi.close();
			}
		}
		
	}


	/**
	 * @param out
	 * @param file
	 * @param path
	 * @throws Exception
	 */
	
	public static void dozipFilesInPath(ZipOutputStream out, File file, String path)
			throws Exception {
		if (file.isDirectory()) {
			File[] ff = file.listFiles();
			if (ff != null) {
				for (File f : ff) {
					if (f.isDirectory()) {
						dozipFilesInPath(out, f, path + PATH_SEP + f.getName());
					} else {
						dozipFilesInPath(out, f, path + PATH_SEP);
					}
				}
			}
		}
		if (file.isFile()) {
			byte[] data = new byte[BUFFER];
			final FileInputStream fi = new FileInputStream(file);
			final BufferedInputStream origin = new BufferedInputStream(fi,
					BUFFER);
			if("".equals(path)){
				out.putNextEntry(new ZipEntry(file.getName()));
			}else{
				out.putNextEntry(new ZipEntry(path + PATH_SEP + file.getName()));
			}
			
			int count;
			while ((count = origin.read(data, 0, BUFFER)) != -1) {
				out.write(data, 0, count);
			}
			origin.close();
			fi.close();
		}
	}
	
	

	/**
	 * Zip with checksum. CRC32
	 * 
	 * @param zipFileName
	 *            the zip file name
	 * @param folderPath
	 *            the folder path
	 * @return the checksum
	 * @throws IOException
	 *             Signals that an I/O exception has occurred.
	 */
	public static long zipFilesInPathWithChecksum(final String zipFileName,final String folderPath) throws IOException {

		final FileOutputStream dest = new FileOutputStream(zipFileName);
		final CheckedOutputStream checkStream = new CheckedOutputStream(dest,new CRC32());
		final ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(checkStream));
		try {
			byte[] data = new byte[BUFFER];
			final File folder = new File(folderPath);
			final List<String> files = Arrays.asList(folder.list());
			for (String file : files) {
				final FileInputStream fi = new FileInputStream(folderPath+ PATH_SEP + file);
				final BufferedInputStream origin = new BufferedInputStream(fi,BUFFER);
				out.putNextEntry(new ZipEntry(file));
				int count;
				while ((count = origin.read(data, 0, BUFFER)) != -1) {
					out.write(data, 0, count);
				}
				origin.close();
			}

		} finally {
			out.close();
			checkStream.close();
			dest.flush();
			dest.close();
		}

		return checkStream.getChecksum().getValue();
	}

	/**
	 * Unzip files to path.
	 * 
	 * @param zipFileName
	 *            the zip file name
	 * @param fileExtractPath
	 *            the file extract path
	 * @throws IOException
	 *             Signals that an I/O exception has occurred.
	 */
	public static void unzipFilesToPath(final String zipFileName,final String fileExtractPath) throws IOException {

		final FileInputStream fis = new FileInputStream(zipFileName);
		final ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
		try {
			ZipEntry entry;

			while ((entry = zis.getNextEntry()) != null) {
				int count;
				byte[] data = new byte[BUFFER];
				final FileOutputStream fos = new FileOutputStream(fileExtractPath + PATH_SEP + entry.getName());
				final BufferedOutputStream dest = new BufferedOutputStream(fos,BUFFER);
				while ((count = zis.read(data, 0, BUFFER)) != -1) {
					dest.write(data, 0, count);
				}
				dest.flush();
				dest.close();
			}
		} finally {
			fis.close();
			zis.close();
		}

	}

	/**
	 * Unzip files to path with checksum. CRC32
	 * 
	 * @param zipFileName
	 *            the zip file name
	 * @param fileExtractPath
	 *            the file extract path
	 * @param checksum
	 *            the checksum
	 * @return true, if checksum matches;
	 * @throws IOException
	 *             Signals that an I/O exception has occurred.
	 */
	public static boolean unzipFilesToPathWithChecksum(final String zipFileName, final String fileExtractPath,
			final long checksum) throws IOException {

		boolean checksumMatches = false;
		final FileInputStream fis = new FileInputStream(zipFileName);
		final CheckedInputStream checkStream = new CheckedInputStream(fis,new CRC32());
		final ZipInputStream zis = new ZipInputStream(new BufferedInputStream(checkStream));

		try {

			ZipEntry entry = null;
			while ((entry = zis.getNextEntry()) != null) {
				int count;
				byte[] data = new byte[BUFFER];
				final FileOutputStream fos = new FileOutputStream(fileExtractPath + PATH_SEP + entry.getName());
				final BufferedOutputStream dest = new BufferedOutputStream(fos,BUFFER);
				while ((count = zis.read(data, 0, BUFFER)) != -1) {
					dest.write(data, 0, count);
				}
				dest.flush();
				dest.close();
			}

		} finally {
			zis.close();
			fis.close();
			checkStream.close();
		}

		if (checkStream.getChecksum().getValue() == checksum) {
			checksumMatches = true;
		}

		return checksumMatches;
	}


}
